// Client-server helpers for TCP/IP
// ClientInfo: wrapper for a socket which throws
//  Receive events, and allows for messaged communication (using
//  a specified character as end-of-message)
// Server: simple TCP server that throws Connect events
// ByteBuilder: utility class to manage byte arrays built up
//  in multiple transactions

// (C) Richard Smith 2005-8
//   bobjanova@gmail.com
// You can use this for free and give it to people as much as you like
// as long as you leave a credit to me :).

// Code to connect to a SOCKS proxy modified from
//   http://www.thecodeproject.com/csharp/ZaSocks5Proxy.asp

// Define this symbol to include console output in various places
//#define DEBUG
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Collections;
using System.Net.Sockets;
using System.Security.Cryptography;
using RedCorona.Cryptography;
using Silmoon;
using Levi.Finance.Client;

//[assembly:System.Reflection.AssemblyVersion("1.4.2008.0330")]

namespace RedCorona.Net
{
	public delegate void ConnectionRead (ClientInfo ci,String text);

	public delegate void ConnectionClosed (ClientInfo ci);

	public delegate void ConnectionReadBytes (ClientInfo ci,byte[] bytes,int len);

	public delegate void ConnectionReadMessage (ClientInfo ci,uint code,byte[] bytes,int len);

	public delegate void ConnectionReadPartialMessage (ClientInfo ci, uint code, byte[] bytes, int start, int read, int sofar, int totallength);

	public delegate void ConnectionNotify (ClientInfo ci);

	public enum ClientDirection
	{
		In,
		Out,
		Left,
		Right,
		Both
	}

	public enum MessageType
	{
		Unmessaged,
		EndMarker,
		Length,
		CodeAndLength
	}
	// ServerDES: The server sends an encryption key on connect
	// ServerRSAClientDES: The server sends an RSA public key, the client sends back a key
	public enum EncryptionType
	{
		None,
		ServerKey,
		ServerRSAClientKey
	}

	public class EncryptionUtils
	{
		static RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider ();

		public static byte[] GetRandomBytes (int length, bool addByte)
		{
			if (addByte && (length > 255))
				throw new ArgumentException ("Length must be 1 byte <256");
			byte[] random = new byte[length + (addByte ? 1 : 0)];
			rng.GetBytes (random);
			if (addByte)
				random [0] = (byte)length;
			return random;
		}
	}

	// OnReadBytes: catch the raw bytes as they arrive
	// OnRead: for text ended with a marker character
	// OnReadMessage: for binary info on a messaged client
	public class ClientInfo
	{
		internal Server server = null;
		private Socket sock;
		private String buffer;

		public event ConnectionRead OnRead;
		public event ConnectionClosed OnClose;
		public event ConnectionReadBytes OnReadBytes;
		public event ConnectionReadMessage OnReadMessage;
		public event ConnectionReadPartialMessage OnPartialMessage;
		public event ConnectionNotify OnReady;

		public MessageType MessageType;
		private ClientDirection dir;
		int id;
		bool alreadyclosed = false;
		public static int NextID = 100;
		//private ClientThread t;
		public object Data = null;

		// Encryption info
		EncryptionType encType;
		int encRead = 0, encStage, encExpected;
		internal bool encComplete;
		internal byte[] encKey;
		internal RSAParameters encParams;

		public EncryptionType EncryptionType {
			get { return encType; }
			set {
				if (encStage != 0)
					throw new ArgumentException ("Key exchange has already begun");
				encType = value;
				encComplete = encType == EncryptionType.None;
				encExpected = -1;
			}
		}

		public bool EncryptionReady {
			get { return encComplete; }
		}

		internal ICryptoTransform encryptor, decryptor;

		public ICryptoTransform Encryptor {
			get { return encryptor; }
		}

		public ICryptoTransform Decryptor {
			get { return decryptor; }
		}

		private string delim;
		public const int BUFSIZE = 1024;
		byte[] buf = new byte[BUFSIZE];
		ByteBuilder bytes = new ByteBuilder (10);
		byte[] msgheader = new byte[8];
		byte headerread = 0;
		bool wantingChecksum = true;

		public string Delimiter {
			get { return delim; }
			set { delim = value; }
		}

		public ClientDirection Direction {
			get { return dir; }
		}

		public Socket Socket {
			get { return sock; }
		}

		public Server Server {
			get { return server; }
		}

		public int ID {
			get { return id; }
		}

		public bool Closed {
			get { return !sock.Connected; }
		}

		public ClientInfo (Socket cl, bool StartNow) : this(cl, null, null, ClientDirection.Both, StartNow, EncryptionType.None)
		{
		}
		//public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d) : this(cl, read, readevt, d, true, EncryptionType.None) {}
		public ClientInfo (Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d, bool StartNow) : this(cl, read, readevt, d, StartNow, EncryptionType.None)
		{
		}

		public ClientInfo (Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d, bool StartNow, EncryptionType encryptionType)
		{
			sock = cl;
			buffer = "";
			OnReadBytes = readevt;
			encType = encryptionType;
			encStage = 0;
			encComplete = encType == EncryptionType.None;
			OnRead = read;
			MessageType = MessageType.EndMarker;
			dir = d;
			delim = "\n";
			id = NextID;
			// Assign each client an application-unique ID
			unchecked {
				NextID++;
			}
			//t = new ClientThread(this);
			if (StartNow)
				BeginReceive ();
		}

		public void BeginReceive ()
		{
//			t.t.Start();
			sock.BeginReceive (buf, 0, BUFSIZE, 0, new AsyncCallback (ReadCallback), this);
		}

		public String Send (String text)
		{
			byte[] ba = Encoding.UTF8.GetBytes (text);
			String s = "";
			for (int i = 0; i < ba.Length; i++)
				s += ba [i] + " ";
			//byte[] ba = {0x00, 0x12, 0x34, 0x56, 0xAA, 0x55, 0xFF};
			Send (ba);
			return s;
		}

		public void SendMessage (uint code, byte[] bytes)
		{
			SendMessage (code, bytes, 0, bytes.Length);
		}

		public void SendMessage (uint code, byte[] bytes, byte paramType)
		{
			SendMessage (code, bytes, paramType, bytes.Length);
		}

		public void SendMessage (uint code, byte[] bytes, byte paramType, int len)
		{
			if (paramType > 0) {
				ByteBuilder b = new ByteBuilder (3);
				b.AddParameter (bytes, paramType);
				bytes = b.Read (0, b.Length);
				len = bytes.Length;
			}
			
			lock (sock) {
				byte checksum = 0;
				byte[] ba;
				switch (MessageType) {
				case MessageType.CodeAndLength:
					Send (ba = UintToBytes (code));
					for (int i = 0; i < 4; i++)
						checksum += ba [i];
					Send (ba = IntToBytes (len));
					for (int i = 0; i < 4; i++)
						checksum += ba [i];
					if (encType != EncryptionType.None)
						Send (new byte[] { checksum });
					break;
				case MessageType.Length:
					Send (ba = IntToBytes (len));
					for (int i = 0; i < 4; i++)
						checksum += ba [i];
					if (encType != EncryptionType.None)
						Send (new byte[] { checksum });
					break;
				}
				Send (bytes, len);
				if (encType != EncryptionType.None) {
					checksum = 0;
					for (int i = 0; i < len; i++)
						checksum += bytes [i];
					Send (new byte[] { checksum });
				}
			}
			
		}

		public void Send (byte[] bytes)
		{
			Send (bytes, bytes.Length);
		}

		public void Send (byte[] bytes, int len)
		{
			if (!encComplete)
				throw new IOException ("Key exchange is not yet completed");
			if (encType != EncryptionType.None) {
				byte[] outbytes = new byte[len];
				Encryptor.TransformBlock (bytes, 0, len, outbytes, 0);
				bytes = outbytes;
				//Console.Write("Encryptor IV: "); LogBytes(encryptor.Key, encryptor.IV.length);
			}
			#if DEBUG
			Console.Write (ID + " Sent: ");
			LogBytes (bytes, len);
			#endif
			sock.Send (bytes, len, SocketFlags.None);
		}

		public bool MessageWaiting ()
		{
			FillBuffer (sock);
			return buffer.IndexOf (delim) >= 0;
		}

		public String Read ()
		{
			//FillBuffer(sock);
			int p = buffer.IndexOf (delim);
			if (p >= 0) {
				String res = buffer.Substring (0, p);
				buffer = buffer.Substring (p + delim.Length);
				return res;
			} else
				return "";
		}

		private void FillBuffer (Socket sock)
		{
			byte[] buf = new byte[256];
			int read;
			while (sock.Available != 0) {
				read = sock.Receive (buf);
				if (OnReadBytes != null)
					OnReadBytes (this, buf, read);
				buffer += Encoding.UTF8.GetString (buf, 0, read);
			}
		}

		void ReadCallback (IAsyncResult ar)
		{
			try {
				int read = sock.EndReceive (ar);
				//Console.WriteLine("Socket "+ID+" read "+read+" bytes");
				if (read > 0) {
					DoRead (buf, read);
					BeginReceive ();
				} else {
					#if DEBUG
					Console.WriteLine (ID + " zero byte read closure");
					#endif
					Close ();
				}
			} catch (SocketException e) {
				#if DEBUG
				Console.WriteLine (ID + " socket exception closure: " + e);
				#endif
				Close ();
			} catch (ObjectDisposedException) {
				#if DEBUG
				Console.WriteLine (ID + " disposed exception closure");
				#endif
				Close ();
			}
		}

		internal void DoRead (byte[] buf, int read)
		{
			if (read > 0) {
				if (OnRead != null) {
					// Simple text mode
					buffer += Encoding.UTF8.GetString (buf, 0, read);
					int i = buffer.IndexOf (delim);
					while (i >= 0)
						OnRead (this, Read ());
				}
			}
			ReadInternal (buf, read, false);
		}

		public static void LogBytes (byte[] buf, int len)
		{
			byte[] ba = new byte[len];
			Array.Copy (buf, ba, len);
			Console.WriteLine (ByteBuilder.FormatParameter (new Parameter (ba, ParameterType.Byte)));
		}

		void ReadInternal (byte[] buf, int read, bool alreadyEncrypted)
		{
			if ((!alreadyEncrypted) && (encType != EncryptionType.None)) {
				if (encComplete) {
					#if DEBUG
					Console.Write (ID + " Received: ");
					LogBytes (buf, read);
					#endif
					buf = decryptor.TransformFinalBlock (buf, 0, read);
					#if DEBUG
					Console.Write (ID + " Decrypted: ");
					LogBytes (buf, read);
					#endif
				} else {
					// Client side key exchange
					int ofs = 0;
					if (encExpected < 0) {
						encStage++;
						ofs++;
						read--;
						encExpected = buf [0];
						// length of key to come
						encKey = new byte[encExpected];
						encRead = 0;
					}
					if (read >= encExpected) {
						Array.Copy (buf, ofs, encKey, encRead, encExpected);
						int togo = read - encExpected;
						encExpected = -1;
						#if DEBUG
						Console.WriteLine (ID + " Read encryption key: " + ByteBuilder.FormatParameter (new Parameter (encKey, ParameterType.Byte)));
						#endif
						if (server == null)
							ClientEncryptionTransferComplete ();
						else
							ServerEncryptionTransferComplete ();
						if (togo > 0) {
							byte[] newbuf = new byte[togo];
							Array.Copy (buf, read + ofs - togo, newbuf, 0, togo);
							ReadInternal (newbuf, togo, false);
						}
					} else {
						Array.Copy (buf, ofs, encKey, encRead, read);
						encExpected -= read;
						encRead += read;
					}
					return;
				}
			}
			
			if ((!alreadyEncrypted) && (OnReadBytes != null))
				OnReadBytes (this, buf, read);
			
			if ((OnReadMessage != null) && (MessageType != MessageType.Unmessaged)) {
				// Messaged mode
				int copied;
				uint code = 0;
				switch (MessageType) {
				case MessageType.CodeAndLength:
				case MessageType.Length:
					int length;
					if (MessageType == MessageType.Length) {
						copied = FillHeader (ref buf, 4, read);
						if (headerread < 4)
							break;
						length = GetInt (msgheader, 0, 4);
					} else {
						copied = FillHeader (ref buf, 8, read);
						if (headerread < 8)
							break;
						code = (uint)GetInt (msgheader, 0, 4);
						length = GetInt (msgheader, 4, 4);
					}
					if (read == copied)
						break;
					// If encryption is on, the next byte is a checksum of the header
					int ofs = 0;
					if (wantingChecksum && (encType != EncryptionType.None)) {
						byte checksum = buf [0];
						ofs++;
						wantingChecksum = false;
						byte headersum = 0;
						for (int i = 0; i < 8; i++)
							headersum += msgheader [i];
						if (checksum != headersum) {
							Close ();
							throw new IOException ("Header checksum failed! (was " + checksum + ", calculated " + headersum + ")");
						}
					}
					bytes.Add (buf, ofs, read - ofs - copied);
					if (encType != EncryptionType.None)
						length++;
					// checksum byte
					// Now we know we are reading into the body of the message
					#if DEBUG
					Console.WriteLine (ID + " Added " + (read - ofs - copied) + " bytes, have " + bytes.Length + " of " + length);
					#endif
					if (OnPartialMessage != null)
						OnPartialMessage (this, code, buf, ofs, read - ofs - copied, bytes.Length, length);
					
					if (bytes.Length >= length) {
						// A message was received!
						headerread = 0;
						wantingChecksum = true;
						byte[] msg = bytes.Read (0, length);
						if (encType != EncryptionType.None) {
							byte checksum = msg [length - 1], msgsum = 0;
							for (int i = 0; i < length - 1; i++)
								msgsum += msg [i];
							if (checksum != msgsum) {
								Close ();
								throw new IOException ("Content checksum failed! (was " + checksum + ", calculated " + msgsum + ")");
							}
							OnReadMessage (this, code, msg, length - 1);
						} else
							OnReadMessage (this, code, msg, length);
						// Don't forget to put the rest through the mill
						int togo = bytes.Length - length;
						if (togo > 0) {
							byte[] whatsleft = bytes.Read (length, togo);
							bytes.Clear ();
							ReadInternal (whatsleft, whatsleft.Length, true);
						} else
							bytes.Clear ();
					}
					//if(OnStatus != null) OnStatus(this, bytes.Length, length);
					break;
				}
			}
		}

		int FillHeader (ref byte[] buf, int to, int read)
		{
			int copied = 0;
			if (headerread < to) {
				// First copy the header into the header variable.
				for (int i = 0; (i < read) && (headerread < to); i++,headerread++,copied++) {
					msgheader [headerread] = buf [i];
				}
			}
			if (copied > 0) {
				// Take the header bytes off the 'message' section
				byte[] newbuf = new byte[read - copied];
				for (int i = 0; i < newbuf.Length; i++)
					newbuf [i] = buf [i + copied];
				buf = newbuf;
			}
			return copied;
		}

		internal ICryptoTransform MakeEncryptor ()
		{
			return MakeCrypto (true);
		}

		internal ICryptoTransform MakeDecryptor ()
		{
			return MakeCrypto (false);
		}

		internal ICryptoTransform MakeCrypto (bool encrypt)
		{
			if (encrypt)
				return new SimpleEncryptor (encKey);
			else
				return new SimpleDecryptor (encKey);
		}

		void ServerEncryptionTransferComplete ()
		{
			switch (encType) {
			case EncryptionType.None:
				throw new ArgumentException ("Should not have key exchange for unencrypted connection!");
			case EncryptionType.ServerKey:
				throw new ArgumentException ("Should not have server-side key exchange for server keyed connection!");
			case EncryptionType.ServerRSAClientKey:
				// Symmetric key is in RSA-encoded encKey
				RSACryptoServiceProvider rsa = new RSACryptoServiceProvider ();
				rsa.ImportParameters (encParams);
				encKey = rsa.Decrypt (encKey, false);
				#if DEBUG
				Console.WriteLine ("Symmetric key is: ");
				LogBytes (encKey, encKey.Length);
				#endif
				MakeEncoders ();
				server.KeyExchangeComplete (this);
				break;
			}
		}

		void ClientEncryptionTransferComplete ()
		{
			// A part of the key exchange process has been completed, and the key is
			// in encKey
			switch (encType) {
			case EncryptionType.None:
				throw new ArgumentException ("Should not have key exchange for unencrypted connection!");
			case EncryptionType.ServerKey:
				// key for transfer is now in encKey, so all is good
				MakeEncoders ();
				break;
			case EncryptionType.ServerRSAClientKey:
				// Stage 1: modulus; Stage 2: exponent
				// When the exponent arrives, create a random DES key
				// and send it
				switch (encStage) {
				case 1:
					encParams.Modulus = encKey;
					break;
				case 2:
					encParams.Exponent = encKey;
					RSACryptoServiceProvider rsa = new RSACryptoServiceProvider ();
					rsa.ImportParameters (encParams);
					encKey = EncryptionUtils.GetRandomBytes (24, false);
					byte[] send = GetLengthEncodedVector (rsa.Encrypt (encKey, false));
					sock.Send (send);
					MakeEncoders ();
					break;
				}

				break;
			}
		}

		internal void MakeEncoders ()
		{
			encryptor = MakeEncryptor ();
			decryptor = MakeDecryptor ();
			if (OnReady != null)
				OnReady (this);
			encComplete = true;
		}

		public static byte[] GetLengthEncodedVector (byte[] @from)
		{
			int l = @from.Length;
			if (l > 255)
				throw new ArgumentException ("Cannot length encode more than 255");
			byte[] to = new byte[l + 1];
			to [0] = (byte)l;
			Array.Copy (@from, 0, to, 1, l);
			return to;
		}

		public static int GetInt (byte[] ba, int @from, int len)
		{
			int r = 0;
			for (int i = 0; i < len; i++)
				r += ba [@from + i] << ((len - i - 1) * 8);
			return r;
		}

		public static int[] GetIntArray (byte[] ba)
		{
			return GetIntArray (ba, 0, ba.Length);
		}

		public static int[] GetIntArray (byte[] ba, int @from, int len)
		{
			int[] res = new int[len / 4];
			for (int i = 0; i < res.Length; i++) {
				res [i] = GetInt (ba, @from + (i * 4), 4);
			}
			return res;
		}

		public static uint[] GetUintArray (byte[] ba)
		{
			uint[] res = new uint[ba.Length / 4];
			for (int i = 0; i < res.Length; i++) {
				res [i] = (uint)GetInt (ba, i * 4, 4);
			}
			return res;
		}

		public static byte[] IntToBytes (int val)
		{
			return UintToBytes ((uint)val);
		}

		public static byte[] UintToBytes (uint val)
		{
			byte[] res = new byte[4];
			for (int i = 3; i >= 0; i--) {
				res [i] = (byte)val;
				val >>= 8;
			}
			return res;
		}

		public static byte[] IntArrayToBytes (int[] val)
		{
			byte[] res = new byte[val.Length * 4];
			for (int i = 0; i < val.Length; i++) {
				byte[] vb = IntToBytes (val [i]);
				res [(i * 4)] = vb [0];
				res [(i * 4) + 1] = vb [1];
				res [(i * 4) + 2] = vb [2];
				res [(i * 4) + 3] = vb [3];
			}
			return res;
		}
//
		public static byte[] UintArrayToBytes (uint[] val)
		{
			byte[] res = new byte[val.Length * 4];
			for (uint i = 0; i < val.Length; i++) {
				byte[] vb = IntToBytes ((int)val [i]);
				res [(i * 4)] = vb [0];
				res [(i * 4) + 1] = vb [1];
				res [(i * 4) + 2] = vb [2];
				res [(i * 4) + 3] = vb [3];
			}
			return res;
		}

		public static byte[] StringArrayToBytes (string[] val, Encoding e)
		{
			byte[] [] baa = new byte[val.Length][];
			int l = 0;
			for (int i = 0; i < val.Length; i++) {
				baa [i] = e.GetBytes (val [i]);
				l += 4 + baa [i].Length;
			}
			byte[] r = new byte[l + 4];
			IntToBytes (val.Length).CopyTo (r, 0);
			int ofs = 4;
			for (int i = 0; i < baa.Length; i++) {
				IntToBytes (baa [i].Length).CopyTo (r, ofs);
				ofs += 4;
				baa [i].CopyTo (r, ofs);
				ofs += baa [i].Length;
			}
			return r;
		}

		public static string[] GetStringArray (byte[] ba, Encoding e)
		{
			int l = GetInt (ba, 0, 4), ofs = 4;
			string[] r = new string[l];
			for (int i = 0; i < l; i++) {
				int thislen = GetInt (ba, ofs, 4);
				ofs += 4;
				r [i] = e.GetString (ba, ofs, thislen);
				ofs += thislen;
			}
			return r;
		}

		public void Close ()
		{
			if (!alreadyclosed) {
				if (server != null)
					server.ClientClosed (this);
				if (OnClose != null)
					OnClose (this);
				alreadyclosed = true;
				#if DEBUG
				Console.WriteLine ("**closed client** at " + DateTime.Now.Ticks);
				#endif
			}
			sock.Close ();
		}
	}

	public class Sockets
	{
		// Socks proxy inspired by http://www.thecodeproject.com/csharp/ZaSocks5Proxy.asp
		public static SocksProxy SocksProxy;
		public static bool UseSocks = false;

		public static Socket CreateTCPSocket (String address, int port)
		{
			return CreateTCPSocket (address, port, UseSocks, SocksProxy);
		}

		public static Socket CreateTCPSocket (String address, int port, bool useSocks, SocksProxy proxy)
		{
			Socket sock;
			if (useSocks)
				sock = ConnectToSocksProxy (proxy.host, proxy.port, address, port, proxy.username, proxy.password);
			else {
				//IPAddress host = Dns.GetHostByName (address).AddressList[0];
				IPAddress host = Dns.GetHostEntry (address).AddressList [0];
				sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
				sock.Connect (new IPEndPoint (host, port));
			}
			return sock;
		}

		private static string[] errorMsgs = { "Operation completed successfully.", "General SOCKS server failure.", "Connection not allowed by ruleset.", "Network unreachable.", "Host unreachable.", "Connection refused.", "TTL expired.", "Command not supported.", "Address type not supported.", "Unknown error." };

		public static Socket ConnectToSocksProxy (IPAddress proxyIP, int proxyPort, String destAddress, int destPort, string userName, string password)
		{
			byte[] request = new byte[257];
			byte[] response = new byte[257];
			ushort nIndex;
			
			IPAddress destIP = null;
			
			try {
				destIP = IPAddress.Parse (destAddress);
			} catch {
			}
			
			IPEndPoint proxyEndPoint = new IPEndPoint (proxyIP, proxyPort);
			
			// open a TCP connection to SOCKS server...
			Socket s;
			s = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			s.Connect (proxyEndPoint);
			/*			} catch(SocketException){
				throw new SocketException(0, "Could not connect to proxy server.");
			}*/			
			
			nIndex = 0;
			request [nIndex++] = 0x05;
			// Version 5.
			request [nIndex++] = 0x02;
			// 2 Authentication methods are in packet...
			request [nIndex++] = 0x00;
			// NO AUTHENTICATION REQUIRED
			request [nIndex++] = 0x02;
			// USERNAME/PASSWORD
			// Send the authentication negotiation request...
			s.Send (request, nIndex, SocketFlags.None);
			
			// Receive 2 byte response...
			int nGot = s.Receive (response, 2, SocketFlags.None);
			if (nGot != 2)
				throw new ConnectionException ("Bad response received from proxy server.");
			
			if (response [1] == 0xFF) {
				// No authentication method was accepted close the socket.
				s.Close ();
				throw new ConnectionException ("None of the authentication method was accepted by proxy server.");
			}
			
			byte[] rawBytes;
			
			if (/*response[1]==0x02*/true) {
				//Username/Password Authentication protocol
				nIndex = 0;
				request [nIndex++] = 0x05;
				// Version 5.
				// add user name
				request [nIndex++] = (byte)userName.Length;
				rawBytes = Encoding.Default.GetBytes (userName);
				rawBytes.CopyTo (request, nIndex);
				nIndex += (ushort)rawBytes.Length;
				
				// add password
				request [nIndex++] = (byte)password.Length;
				rawBytes = Encoding.Default.GetBytes (password);
				rawBytes.CopyTo (request, nIndex);
				nIndex += (ushort)rawBytes.Length;
				
				// Send the Username/Password request
				s.Send (request, nIndex, SocketFlags.None);
				// Receive 2 byte response...
				nGot = s.Receive (response, 2, SocketFlags.None);
				if (nGot != 2)
					throw new ConnectionException ("Bad response received from proxy server.");
				if (response [1] != 0x00)
					throw new ConnectionException ("Bad Usernaem/Password.");
			}
			// This version only supports connect command. 
			// UDP and Bind are not supported.
			
			// Send connect request now...
			nIndex = 0;
			request [nIndex++] = 0x05;
			// version 5.
			request [nIndex++] = 0x01;
			// command = connect.
			request [nIndex++] = 0x00;
			// Reserve = must be 0x00
			if (destIP != null) {
				// Destination adress in an IP.
				switch (destIP.AddressFamily) {
				case AddressFamily.InterNetwork:
					// Address is IPV4 format
					request [nIndex++] = 0x01;
					rawBytes = destIP.GetAddressBytes ();
					rawBytes.CopyTo (request, nIndex);
					nIndex += (ushort)rawBytes.Length;
					break;
				case AddressFamily.InterNetworkV6:
					// Address is IPV6 format
					request [nIndex++] = 0x04;
					rawBytes = destIP.GetAddressBytes ();
					rawBytes.CopyTo (request, nIndex);
					nIndex += (ushort)rawBytes.Length;
					break;
				}
			} else {
				// Dest. address is domain name.
				request [nIndex++] = 0x03;
				// Address is full-qualified domain name.
				request [nIndex++] = Convert.ToByte (destAddress.Length);
				// length of address.
				rawBytes = Encoding.Default.GetBytes (destAddress);
				rawBytes.CopyTo (request, nIndex);
				nIndex += (ushort)rawBytes.Length;
			}
			
			// using big-edian byte order
			byte[] portBytes = BitConverter.GetBytes ((ushort)destPort);
			for (int i = portBytes.Length - 1; i >= 0; i--)
				request [nIndex++] = portBytes [i];
			
			// send connect request.
			s.Send (request, nIndex, SocketFlags.None);
			s.Receive (response);
			// Get variable length response...
			if (response [1] != 0x00)
				throw new ConnectionException (errorMsgs [response [1]]);
			// Success Connected...
			return s;
		}
	}

	public struct SocksProxy
	{
		public IPAddress host;
		public ushort port;
		public string username, password;

		public SocksProxy (String hostname, ushort port, String username, String password)
		{
			this.port = port;
			//host = Dns.GetHostByName (hostname).AddressList[0];
			host = Dns.GetHostEntry (hostname).AddressList [0];
			this.username = username;
			this.password = password;
		}
	}

	public class ConnectionException : Exception
	{
		public ConnectionException (string message) : base(message)
		{
		}
	}

	// Server code cribbed from Framework Help
	public delegate bool ClientEvent (Server serv, ClientInfo new_client);
	// whether to accept the client
	public class Server : Silmoon.MySilmoon.IRunningAble, IDisposable
	{
		class ClientState
		{
			// To hold the state information about a client between transactions
			internal Socket Socket = null;
			internal const int BufferSize = 1024;
			internal byte[] buffer = new byte[BufferSize];
			internal StringBuilder sofar = new StringBuilder ();

			internal ClientState (Socket sock)
			{
				Socket = sock;
			}
		}

		ArrayList clients = new ArrayList ();
		Socket ss;

		public event ClientEvent Connect;
		public event ClientEvent ClientReady;

		public ABC g;
		IPEndPoint endPoint = null;
		
		public IEnumerable Clients {
			get { return clients; }
		}

		public Socket ServerSocket {
			get { return ss; }
		}

		public ClientInfo this [int id] {
			get {
				foreach (ClientInfo ci in Clients)
					if (ci.ID == id)
						return ci;
				return null;
			}
		}

		private EncryptionType encType;

		public EncryptionType DefaultEncryptionType {
			get { return encType; }
			set { encType = value; }
		}

		public int Port {
			get { return ((IPEndPoint)ss.LocalEndPoint).Port; }
		}

		public Server (ABC _g) : this(_g, null)
		{
		}

		public Server (ABC _g, ClientEvent connDel)
		{
			this.Connect = connDel;
			this.g = _g;
		}
		
		public void startSocket ()
		{
			//ss = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			//ss.Bind (new IPEndPoint (IPAddress.Any, port));
			//ss.Listen (100);
			ss = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			if (string.IsNullOrEmpty (g.configure.server_net_bindip))
				endPoint = new IPEndPoint (IPAddress.Any, int.Parse (g.configure.server_net_port));
			else
				endPoint = new IPEndPoint (IPAddress.Parse (g.configure.server_net_bindip), int.Parse (g.configure.server_net_port));

			g.onOutputText ("tcp socket bind endpoint:" + endPoint.ToString ());

			ss.Bind ((EndPoint)endPoint);
			g.onOutputText ("tcp socket start up...", 1);
			ss.Listen (100);
			g.onOutputText ("tcp start listen...", 1);
			// Start the accept process. When a connection is accepted, the callback
			// must do this again to accept another connection
			ss.BeginAccept (new AsyncCallback (AcceptCallback), ss);
		}

		internal void ClientClosed (ClientInfo ci)
		{
			clients.Remove (ci);
		}

		public void Broadcast (byte[] bytes)
		{
			foreach (ClientInfo ci in clients)
				ci.Send (bytes);
		}

		public void BroadcastMessage (uint code, byte[] bytes)
		{
			BroadcastMessage (code, bytes, 0);
		}

		public void BroadcastMessage (uint code, byte[] bytes, byte paramType)
		{
			foreach (ClientInfo ci in clients)
				ci.SendMessage (code, bytes, paramType);
		}

		// ASYNC CALLBACK CODE
		void AcceptCallback (IAsyncResult ar)
		{
			try {
				Socket server = (Socket)ar.AsyncState;
				Socket cs = server.EndAccept (ar);
				
				// Start the thing listening again
				server.BeginAccept (new AsyncCallback (AcceptCallback), server);
				
				ClientInfo c = new ClientInfo (cs, null, null, ClientDirection.Both, false);
				c.server = this;
				c.MessageType = MessageType.CodeAndLength;
				// Allow the new client to be rejected by the application
				if (Connect != null) {
					if (!Connect (this, c)) {
						// Rejected
						cs.Close ();
						return;
					}
				}
				// Initiate key exchange
				c.EncryptionType = encType;
				switch (encType) {
				case EncryptionType.None:
					KeyExchangeComplete (c);
					break;
				case EncryptionType.ServerKey:
					c.encKey = GetSymmetricKey ();
					byte[] key = ClientInfo.GetLengthEncodedVector (c.encKey);
					cs.Send (key);
					c.MakeEncoders ();
					KeyExchangeComplete (c);
					break;
				case EncryptionType.ServerRSAClientKey:
					RSACryptoServiceProvider rsa = new RSACryptoServiceProvider ();
					RSAParameters p = rsa.ExportParameters (true);
					cs.Send (ClientInfo.GetLengthEncodedVector (p.Modulus));
					cs.Send (ClientInfo.GetLengthEncodedVector (p.Exponent));
					c.encParams = p;
					break;
				default:
					throw new ArgumentException ("Unknown or unsupported encryption type " + encType);
				}
				clients.Add (c);
				c.BeginReceive ();
			} catch (ObjectDisposedException) {
			} catch (SocketException) {
			} catch (Exception e) {
				Console.WriteLine (e);
			}
		}

		protected virtual byte[] GetSymmetricKey ()
		{
			return EncryptionUtils.GetRandomBytes (24, false);
		}

		internal void KeyExchangeComplete (ClientInfo ci)
		{
			// Key exchange is complete on this client. Client ready
			// handlers may still force a close of the connection
			if (ClientReady != null)
			if (!ClientReady (this, ci))
				ci.Close ();
		}

		~Server ()
		{
			Close ();
		}

		public void Close ()
		{
			ArrayList cl2 = new ArrayList ();
			foreach (ClientInfo c in clients)
				cl2.Add (c);
			foreach (ClientInfo c in cl2)
				c.Close ();
			
			ss.Close ();
		}
		
		#region IRunningAble 成员

        public bool Resume()
        {
            return false;
        }
        Silmoon.MySilmoon.RunningState _runningState = Silmoon.MySilmoon.RunningState.Stopped;
        public Silmoon.MySilmoon.RunningState RunningState
        {
            get { return _runningState; }
            private set { _runningState = value; }
        }

        public bool Start()
        {
            if (RunningState == Silmoon.MySilmoon.RunningState.Running) return false;
            RunningState = Silmoon.MySilmoon.RunningState.Running;
            startSocket();
            return true;
        }

        public void StartA()
        {
            Start();
        }

        public bool Stop()
        {
            if (RunningState == Silmoon.MySilmoon.RunningState.Stopped) return false;
            RunningState = Silmoon.MySilmoon.RunningState.Stopped;
            Close();
            return true;
        }

        public bool Suspend()
        {
            return false;
        }

        #endregion

        #region IDisposable 成员

        public void Dispose()
        {
            Stop();
            ss = null;
        }

        #endregion
	}

	public class ByteBuilder : MarshalByRefObject
	{
		byte[][] data;
		int packsize, used;

		public int Length {
			get {
				int len = 0;
				for (int i = 0; i < used; i++)
					len += data [i].Length;
				return len;
			}
		}

		public byte this [int i] {
			get { return Read (i, 1) [0]; }
		}

		public ByteBuilder () : this(10)
		{
		}

		public ByteBuilder (int packsize)
		{
			this.packsize = packsize;
			used = 0;
			data = new byte[packsize][];
		}

		public ByteBuilder (byte[] data)
		{
			packsize = 1;
			used = 1;
			this.data = new byte[][] { data };
		}

		public ByteBuilder (byte[] data, int len) : this(data, 0, len)
		{
		}

		public ByteBuilder (byte[] data, int @from, int len) : this(1)
		{
			Add (data, @from, len);
		}

		public void Add (byte[] moredata)
		{
			Add (moredata, 0, moredata.Length);
		}

		public void Add (byte[] moredata, int @from, int len)
		{
//Console.WriteLine("Getting "+from+" to "+(from+len-1)+" of "+moredata.Length);
			if (used < packsize) {
				data [used] = new byte[len];
				for (int j = @from; j < @from + len; j++)
					data [used] [j - @from] = moredata [j];
				used++;
			} else {
				// Compress the existing items into the first array
				byte[] newdata = new byte[Length + len];
				int np = 0;
				for (int i = 0; i < used; i++)
					for (int j = 0; j < data[i].Length; j++)
						newdata [np++] = data [i] [j];
				for (int j = @from; j < @from + len; j++)
					newdata [np++] = moredata [j];
				data [0] = newdata;
				for (int i = 1; i < used; i++)
					data [i] = null;
				used = 1;
			}
		}

		public byte[] Read (int @from, int len)
		{
			if (len == 0)
				return new byte[0];
			byte[] res = new byte[len];
			int done = 0, start = 0;
			
			for (int i = 0; i < used; i++) {
				if ((start + data [i].Length) <= @from) {
					start += data [i].Length;
					continue;
				}
				// Now we're in the data block
				for (int j = 0; j < data[i].Length; j++) {
					if ((j + start) < @from)
						continue;
					res [done++] = data [i] [j];
					if (done == len)
						return res;
				}
			}
			
			throw new ArgumentException ("Datapoints " + @from + " and " + (@from + len) + " must be less than " + Length);
		}

		public void Clear ()
		{
			used = 0;
			for (int i = 0; i < used; i++)
				data [i] = null;
		}

		public Parameter GetParameter (ref int index)
		{
			Parameter res = new Parameter ();
			res.Type = Read (index++, 1) [0];
			byte[] lenba = Read (index, 4);
			index += 4;
			int len = ClientInfo.GetInt (lenba, 0, 4);
			res.content = Read (index, len);
			index += len;
			return res;
		}

		public void AddParameter (Parameter param)
		{
			AddParameter (param.content, param.Type);
		}

		public void AddParameter (byte[] content, byte Type)
		{
			Add (new byte[] { Type });
			Add (ClientInfo.IntToBytes (content.Length));
			Add (content);
		}

		public static String FormatParameter (Parameter p)
		{
			switch (p.Type) {
			case ParameterType.Int:
				int[] ia = ClientInfo.GetIntArray (p.content);
				StringBuilder sb = new StringBuilder ();
				foreach (int i in ia)
					sb.Append (i + " ");
				return sb.ToString ();
			case ParameterType.Uint:
				ia = ClientInfo.GetIntArray (p.content);
				sb = new StringBuilder ();
				foreach (int i in ia)
					sb.Append (i.ToString ("X8") + " ");
				return sb.ToString ();
			case ParameterType.String:
				return Encoding.UTF8.GetString (p.content);
			case ParameterType.StringArray:
				string[] sa = ClientInfo.GetStringArray (p.content, Encoding.UTF8);
				sb = new StringBuilder ();
				foreach (string s in sa)
					sb.Append (s + "; ");
				return sb.ToString ();
			case ParameterType.Byte:
				sb = new StringBuilder ();
				foreach (int b in p.content)
					sb.Append (b.ToString ("X2") + " ");
				return sb.ToString ();
			default:
				return "??";
			}
		}
	}

	[Serializable]
	public struct Parameter
	{
		public byte Type;
		public byte[] content;

		public Parameter (byte[] content, byte type)
		{
			this.content = content;
			Type = type;
		}
	}

	public struct ParameterType
	{
		public const byte Unparameterised = 0;
		public const byte Int = 1;
		public const byte Uint = 2;
		public const byte String = 3;
		public const byte Byte = 4;
		public const byte StringArray = 5;
	}
}

namespace RedCorona.Cryptography
{
	// Cryptographic classes
	public abstract class BaseCrypto : ICryptoTransform
	{
		public int InputBlockSize {
			get { return 1; }
		}

		public int OutputBlockSize {
			get { return 1; }
		}

		public bool CanTransformMultipleBlocks {
			get { return true; }
		}

		public bool CanReuseTransform {
			get { return true; }
		}

		protected byte[] key;
		protected byte currentKey;
		protected int done = 0, keyinx = 0 ;

		protected BaseCrypto (byte[] key)
		{
			if (key.Length == 0)
				throw new ArgumentException ("Must provide a key");
			this.key = key;
			currentKey = 0;
			for (int i = 0; i < key.Length; i++)
				currentKey += key [i];
		}

		protected abstract byte DoByte (byte b)
;
		public int TransformBlock (byte[] @from, int frominx, int len, byte[] to, int toinx)
		{
			for (int i = 0; i < len; i++) {
				byte oldkey = currentKey;
				to [toinx + i] = DoByte (@from [frominx + i]);
				#if DEBUG
//				Console.WriteLine("  encrypting "+from[frominx + i]+" to "+to[toinx + i]+", key is "+oldkey);
				#endif
				BumpKey ();
			}
			return len;
		}

		public byte[] TransformFinalBlock (byte[] @from, int frominx, int len)
		{
			byte[] to = new byte[len];
			TransformBlock (@from, frominx, len, to, 0);
			return to;
		}

		protected void BumpKey ()
		{
			keyinx = (keyinx + 1) % key.Length;
			currentKey = Multiply257 (key [keyinx], currentKey);
		}

		protected static byte Multiply257 (byte a, byte b)
		{
			return (byte)((((a + 1) * (b + 1)) % 257) - 1);
		}

		protected static byte[] complements = { 0, 128, 85, 192, 102, 42, 146, 224, 199, 179,
		186, 149, 177, 201, 119, 240, 120, 99, 229, 89,
		48, 221, 189, 74, 71, 88, 237, 100, 194, 59,
		198, 248, 147, 188, 234, 49, 131, 114, 144, 44,
		162, 152, 5, 110, 39, 94, 174, 165, 20, 35,
		125, 172, 96, 118, 242, 178, 247, 225, 60, 29,
		58, 227, 101, 252, 86, 73, 233, 222, 148, 245,
		180, 24, 168, 65, 23, 185, 246, 200, 243, 150,
		164, 209, 95, 204, 126, 2, 64, 183, 25, 19,
		208, 175, 151, 215, 45, 82, 52, 138, 134, 17,
		27, 62, 4, 214, 163, 176, 244, 187, 223, 249,
		43, 217, 115, 123, 37, 112, 133, 158, 53, 14,
		16, 157, 139, 113, 219, 50, 84, 254, 1, 171,
		205, 36, 142, 116, 98, 239, 241, 202, 97, 122,
		143, 218, 132, 140, 38, 212, 6, 32, 68, 11,
		79, 92, 41, 251, 193, 228, 238, 121, 117, 203,
		173, 210, 40, 104, 80, 47, 236, 230, 72, 191,
		253, 129, 51, 160, 46, 91, 105, 12, 55, 9,
		70, 232, 190, 87, 231, 75, 10, 107, 33, 22,
		182, 169, 3, 154, 28, 197, 226, 195, 30, 8,
		77, 13, 137, 159, 83, 130, 220, 235, 90, 81,
		161, 216, 145, 250, 103, 93, 211, 111, 141, 124,
		206, 21, 67, 108, 7, 57, 196, 61, 155, 18,
		167, 184, 181, 66, 34, 207, 166, 26, 156, 135,
		15, 136, 54, 78, 106, 69, 76, 56, 31, 109,
		213, 153, 63, 170, 127, 255 };

		protected static byte Complement257 (byte b)
		{
			return complements [b];
		}

		public void Dispose ()
		{
		}
		// for IDisposable
	}

	public class SimpleEncryptor : BaseCrypto
	{
		public SimpleEncryptor (byte[] key) : base(key)
		{
		}

		protected override byte DoByte (byte b)
		{
			byte b2 = Multiply257 ((byte)(b + currentKey), currentKey);
			currentKey = Multiply257 ((byte)(b + b2), currentKey);
			return b2;
		}
	}

	public class SimpleDecryptor : BaseCrypto
	{
		public SimpleDecryptor (byte[] key) : base(key)
		{
		}

		protected override byte DoByte (byte b)
		{
			byte b2 = (byte)(Multiply257 (b, Complement257 (currentKey)) - currentKey);
			currentKey = Multiply257 ((byte)(b + b2), currentKey);
			return b2;
		}
	}
}
